﻿// <copyright>
// Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Mvc;
using CloudMLDemo.Models;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;

namespace CloudMLDemo.Controllers
{
    public class HomeController : Controller
    {
        public string WebServiceODataEndpoint;

        public string WebServiceKey;

        public bool IsSuccessful;

        private const string StorageAccountName = @"[your storage account name]";

        private const string StorageAccountKey = @"[your storage account key]";

        private readonly Uri _storageBaseUrl = new Uri("https://" + StorageAccountName + ".blob.core.windows.net");

        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

        //
        // POST: /Home/Validate

        [HttpPost]
        public ActionResult Validate(FormCollection collection)
        {
            var view = View();
            WebServiceODataEndpoint = collection["WSEndpoint"];
            WebServiceKey = collection["WSKey"];
            view.ViewData["WSKey"] = WebServiceKey;
            IsSuccessful = true;
            List<string> webServiceInputParameters;
            // Validate that schema from csv matches that of webService
            try
            {
                var metadata = DownloadModelMetaData(WebServiceODataEndpoint);
                webServiceInputParameters = GetModelInputNames(metadata).ToList();
            }
            catch (Exception)
            {

                view.ViewData["ValidationMessage"] = "Cannot reach Web Service endpoint";
                IsSuccessful = false;
                return view;
            }
            var file = Request.Files["csvFile"];
            using (var sr = new StreamReader(file.InputStream))
            {
                var title = sr.ReadLine().Split(',').Select(t => t.Trim(' '));
                if (!webServiceInputParameters.SequenceEqual(title))
                {
                    view.ViewData["ValidationMessage"] =
                        "Csv column names does not match Web Service input metadata";
                    IsSuccessful = false;
                    return view;
                }

                view.ViewData["ValidationMessage"] =
                    "Data schema matches, please proceed!";

                // save the file into blob
                var blobClient = new CloudBlobClient(_storageBaseUrl,
                    new StorageCredentials(StorageAccountName, StorageAccountKey));
                CloudBlobContainer container = blobClient.GetContainerReference("demojobs");
                container.CreateIfNotExists();
                container.SetPermissions(new BlobContainerPermissions
                {
                    PublicAccess = BlobContainerPublicAccessType.Blob
                });

                // Pull data from csv and write to .arff
                var inputBlobName = Guid.NewGuid() + ".arff";
                CloudBlockBlob cloudBlob = container.GetBlockBlobReference(inputBlobName);
                using (var blobStream = cloudBlob.OpenWrite())
                {
                    var inputStream = new StreamWriter(blobStream);
                    inputStream.WriteLine("@RELATION	Unnamed");
                    webServiceInputParameters.ForEach(
                        param => inputStream.WriteLine("@ATTRIBUTE	" + param + "	NUMERIC"));

                    inputStream.WriteLine("@DATA");
                    inputStream.Write(sr.ReadToEnd());
                    inputStream.Flush();
                }

                view.ViewData["InputBlob"] = container.Name + "/" + inputBlobName;
            }

            return view;
        }

        // POST: /Home/RunJob
        [HttpPost]
        public async Task<ActionResult> RunJob(FormCollection collection)
        {
            var view = View();
            WebServiceODataEndpoint = collection["WSEndpoint"];
            WebServiceKey = collection["WSKey"];
            var inputBlob = collection["inputblob"];
            IsSuccessful = true;
            // Submit the job
            var input = new AzureBlobDataReference
            {
                ConnectionString = "DefaultEndpointsProtocol=https;AccountName=" + StorageAccountName + ";AccountKey=" + StorageAccountKey,
                RelativeLocation = inputBlob
            };

            var output = new AzureBlobDataReference();

            var scoreAddress = WebServiceODataEndpoint.Replace("/odata/workspaces/", "/workspaces/").TrimEnd('/') + "/jobs";
            var jobId = string.Empty;
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + WebServiceKey);
                var response = await client.PostAsJsonAsync(scoreAddress, new BatchScoreRequest { Input = input, Output = output }).ConfigureAwait(false);
                if (response.IsSuccessStatusCode)
                {
                    var job = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    jobId = job.Trim('"');
                }
                else
                {
                    IsSuccessful = false;
                }
            }

            view.ViewData["jobAddress"] = scoreAddress + "/" + jobId;

            return view;
        }

        // POST: /Home/DownloadJob
        [HttpPost]
        public async Task<ActionResult> DownloadJob(FormCollection collection)
        {
            var view = View();
            var jobAddress = collection["jobAddress"];
            WebServiceKey = collection["WSKey"];
            IsSuccessful = true;
            using (var client = new WebClient())
            {
                client.Headers.Add("Authorization", "Bearer " + WebServiceKey);
                var jsonString = client.DownloadString(jobAddress);
                BatchScoreStatus status = await JsonConvert.DeserializeObjectAsync<BatchScoreStatus>(jsonString);
                if (status.StatusCode != BatchScoreStatusCode.Finished)
                {
                    IsSuccessful = false;
                }
                else
                {
                    view.ViewData["downloadAddress"] = status.Result.BaseLocation + status.Result.RelativeLocation +
                                          status.Result.SasBlobToken;
                }
            }

            return view;

        }

        // POST: /Home/CheckJob
        [HttpPost]
        public ActionResult CheckJob(FormCollection collection)
        {
            var view = View();
            var jobAddress = collection["jobAddress"];
            WebServiceKey = collection["WSKey"];
            using (var client = new WebClient())
            {
                client.Headers.Add("Authorization", "Bearer " + WebServiceKey);
                var jsonString = client.DownloadString(jobAddress);
                view.ViewData["JobStatus"] = jsonString;
            }
            return view;
        }


        private string DownloadModelMetaData(string endpoint)
        {
            string metadataPath = endpoint.TrimEnd('/') + "/$metadata";

            using (var client = new WebClient())
            {
                return client.DownloadString(new Uri(metadataPath));
            }
        }

        private IEnumerable<string> GetModelInputNames(string metadata)
        {
            var regex = new Regex(@"<Parameter Name=.*");
            var matches = regex.Matches(metadata);
            return from object match in matches select match.ToString().Split('"')[1];
        }
    }
}
